// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../../include/fpdfapi/fpdf_parser.h"
#include "../../../include/fpdfapi/fpdf_module.h"
extern FX_LPVOID PDFPreviewInitCache(CPDF_Document* pDoc);
extern void PDFPreviewClearCache(FX_LPVOID pCache);
CPDF_Document::CPDF_Document(IPDF_DocParser* pParser) : CPDF_IndirectObjects(pParser)
{
    ASSERT(pParser != NULL);
    m_pRootDict = NULL;
    m_pInfoDict = NULL;
    m_bLinearized = FALSE;
    m_dwFirstPageNo = 0;
    m_dwFirstPageObjNum = 0;
    m_pDocPage = CPDF_ModuleMgr::Get()->GetPageModule()->CreateDocData(this);
    m_pDocRender = CPDF_ModuleMgr::Get()->GetRenderModule()->CreateDocData(this);
}
CPDF_DocPageData* CPDF_Document::GetValidatePageData()
{
    if (m_pDocPage) {
        return m_pDocPage;
    }
    m_pDocPage = CPDF_ModuleMgr::Get()->GetPageModule()->CreateDocData(this);
    return m_pDocPage;
}
CPDF_DocRenderData* CPDF_Document::GetValidateRenderData()
{
    if (m_pDocRender) {
        return m_pDocRender;
    }
    m_pDocRender = CPDF_ModuleMgr::Get()->GetRenderModule()->CreateDocData(this);
    return m_pDocRender;
}
void CPDF_Document::LoadDoc()
{
    m_LastObjNum = m_pParser->GetLastObjNum();
    CPDF_Object* pRootObj = GetIndirectObject(m_pParser->GetRootObjNum());
    if (pRootObj == NULL) {
        return;
    }
    m_pRootDict = pRootObj->GetDict();
    if (m_pRootDict == NULL) {
        return;
    }
    CPDF_Object* pInfoObj = GetIndirectObject(m_pParser->GetInfoObjNum());
    if (pInfoObj) {
        m_pInfoDict = pInfoObj->GetDict();
    }
    CPDF_Array* pIDArray = m_pParser->GetIDArray();
    if (pIDArray) {
        m_ID1 = pIDArray->GetString(0);
        m_ID2 = pIDArray->GetString(1);
    }
    m_PageList.SetSize(_GetPageCount());
}
void CPDF_Document::LoadAsynDoc(CPDF_Dictionary *pLinearized)
{
    m_bLinearized = TRUE;
    m_LastObjNum = m_pParser->GetLastObjNum();
    CPDF_Object* indirectObj = GetIndirectObject(m_pParser->GetRootObjNum());
    m_pRootDict = indirectObj ? indirectObj->GetDict() : NULL;
    if (m_pRootDict == NULL) {
        return;
    }
    indirectObj = GetIndirectObject(m_pParser->GetInfoObjNum());
    m_pInfoDict = indirectObj ? indirectObj->GetDict() : NULL;
    CPDF_Array* pIDArray = m_pParser->GetIDArray();
    if (pIDArray) {
        m_ID1 = pIDArray->GetString(0);
        m_ID2 = pIDArray->GetString(1);
    }
    FX_DWORD dwPageCount = 0;
    CPDF_Object *pCount = pLinearized->GetElement(FX_BSTRC("N"));
    if (pCount && pCount->GetType() == PDFOBJ_NUMBER) {
        dwPageCount = pCount->GetInteger();
    }
    m_PageList.SetSize(dwPageCount);
    CPDF_Object *pNo = pLinearized->GetElement(FX_BSTRC("P"));
    if (pNo && pNo->GetType() == PDFOBJ_NUMBER) {
        m_dwFirstPageNo = pNo->GetInteger();
    }
    CPDF_Object *pObjNum = pLinearized->GetElement(FX_BSTRC("O"));
    if (pObjNum && pObjNum->GetType() == PDFOBJ_NUMBER) {
        m_dwFirstPageObjNum = pObjNum->GetInteger();
    }
}
void CPDF_Document::LoadPages()
{
    m_PageList.SetSize(_GetPageCount());
}
extern void FPDF_TTFaceMapper_ReleaseDoc(CPDF_Document*);
CPDF_Document::~CPDF_Document()
{
    if (m_pDocRender) {
        CPDF_ModuleMgr::Get()->GetRenderModule()->DestroyDocData(m_pDocRender);
    }
    if (m_pDocPage) {
        CPDF_ModuleMgr::Get()->GetPageModule()->ReleaseDoc(this);
        CPDF_ModuleMgr::Get()->GetPageModule()->ClearStockFont(this);
    }
}
#define		FX_MAX_PAGE_LEVEL			1024
CPDF_Dictionary* CPDF_Document::_FindPDFPage(CPDF_Dictionary* pPages, int iPage, int nPagesToGo, int level)
{
    CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids"));
    if (pKidList == NULL) {
        if (nPagesToGo == 0) {
            return pPages;
        }
        return NULL;
    }
    if (level >= FX_MAX_PAGE_LEVEL) {
        return NULL;
    }
    int nKids = pKidList->GetCount();
    for (int i = 0; i < nKids; i ++) {
        CPDF_Dictionary* pKid = pKidList->GetDict(i);
        if (pKid == NULL) {
            nPagesToGo --;
            continue;
        }
        if (pKid == pPages) {
            continue;
        }
        if (!pKid->KeyExist(FX_BSTRC("Kids"))) {
            if (nPagesToGo == 0) {
                return pKid;
            }
            m_PageList.SetAt(iPage - nPagesToGo, pKid->GetObjNum());
            nPagesToGo --;
        } else {
            int nPages = pKid->GetInteger(FX_BSTRC("Count"));
            if (nPagesToGo < nPages) {
                return _FindPDFPage(pKid, iPage, nPagesToGo, level + 1);
            }
            nPagesToGo -= nPages;
        }
    }
    return NULL;
}
CPDF_Dictionary* CPDF_Document::GetPage(int iPage)
{
    if (iPage < 0 || iPage >= m_PageList.GetSize()) {
        return NULL;
    }
    if (m_bLinearized && (iPage == (int)m_dwFirstPageNo)) {
        CPDF_Object* pObj = GetIndirectObject(m_dwFirstPageObjNum);
        if (pObj && pObj->GetType() == PDFOBJ_DICTIONARY) {
            return (CPDF_Dictionary*)pObj;
        }
    }
    int objnum = m_PageList.GetAt(iPage);
    if (objnum) {
        CPDF_Object* pObj = GetIndirectObject(objnum);
        ASSERT(pObj->GetType() == PDFOBJ_DICTIONARY);
        return (CPDF_Dictionary*)pObj;
    }
    CPDF_Dictionary* pRoot = GetRoot();
    if (pRoot == NULL) {
        return NULL;
    }
    CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));
    if (pPages == NULL) {
        return NULL;
    }
    CPDF_Dictionary* pPage = _FindPDFPage(pPages, iPage, iPage, 0);
    if (pPage == NULL) {
        return NULL;
    }
    m_PageList.SetAt(iPage, pPage->GetObjNum());
    return pPage;
}
int CPDF_Document::_FindPageIndex(CPDF_Dictionary* pNode, FX_DWORD& skip_count, FX_DWORD objnum, int& index, int level)
{
    if (pNode->KeyExist(FX_BSTRC("Kids"))) {
        CPDF_Array* pKidList = pNode->GetArray(FX_BSTRC("Kids"));
        if (pKidList == NULL) {
            return -1;
        }
        if (level >= FX_MAX_PAGE_LEVEL) {
            return -1;
        }
        FX_DWORD count = pNode->GetInteger(FX_BSTRC("Count"));
        if (count <= skip_count) {
            skip_count -= count;
            index += count;
            return -1;
        }
        if (count && count == pKidList->GetCount()) {
            for (FX_DWORD i = 0; i < count; i ++) {
                CPDF_Reference* pKid = (CPDF_Reference*)pKidList->GetElement(i);
                if (pKid && pKid->GetType() == PDFOBJ_REFERENCE) {
                    if (pKid->GetRefObjNum() == objnum) {
                        m_PageList.SetAt(index + i, objnum);
                        return index + i;
                    }
                }
            }
        }
        for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) {
            CPDF_Dictionary* pKid = pKidList->GetDict(i);
            if (pKid == NULL) {
                continue;
            }
            if (pKid == pNode) {
                continue;
            }
            int found_index = _FindPageIndex(pKid, skip_count, objnum, index, level + 1);
            if (found_index >= 0) {
                return found_index;
            }
        }
    } else {
        if (objnum == pNode->GetObjNum()) {
            return index;
        }
        if (skip_count) {
            skip_count--;
        }
        index ++;
    }
    return -1;
}
int CPDF_Document::GetPageIndex(FX_DWORD objnum)
{
    FX_DWORD nPages = m_PageList.GetSize();
    FX_DWORD skip_count = 0;
    FX_BOOL bSkipped = FALSE;
    for (FX_DWORD i = 0; i < nPages; i ++) {
        FX_DWORD objnum1 = m_PageList.GetAt(i);
        if (objnum1 == objnum) {
            return i;
        }
        if (!bSkipped && objnum1 == 0) {
            skip_count = i;
            bSkipped = TRUE;
        }
    }
    CPDF_Dictionary* pRoot = GetRoot();
    if (pRoot == NULL) {
        return -1;
    }
    CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));
    if (pPages == NULL) {
        return -1;
    }
    int index = 0;
    return _FindPageIndex(pPages, skip_count, objnum, index);
}
int CPDF_Document::GetPageCount() const
{
    return m_PageList.GetSize();
}
static int _CountPages(CPDF_Dictionary* pPages, int level)
{
    if (level > 128) {
        return 0;
    }
    int count = pPages->GetInteger(FX_BSTRC("Count"));
    if (count > 0 && count < FPDF_PAGE_MAX_NUM) {
        return count;
    }
    CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids"));
    if (pKidList == NULL) {
        return 0;
    }
    count = 0;
    for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) {
        CPDF_Dictionary* pKid = pKidList->GetDict(i);
        if (pKid == NULL) {
            continue;
        }
        if (!pKid->KeyExist(FX_BSTRC("Kids"))) {
            count ++;
        } else {
            count += _CountPages(pKid, level + 1);
        }
    }
    pPages->SetAtInteger(FX_BSTRC("Count"), count);
    return count;
}
int CPDF_Document::_GetPageCount() const
{
    CPDF_Dictionary* pRoot = GetRoot();
    if (pRoot == NULL) {
        return 0;
    }
    CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));
    if (pPages == NULL) {
        return 0;
    }
    if (!pPages->KeyExist(FX_BSTRC("Kids"))) {
        return 1;
    }
    return _CountPages(pPages, 0);
}
static FX_BOOL _EnumPages(CPDF_Dictionary* pPages, IPDF_EnumPageHandler* pHandler)
{
    CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids"));
    if (pKidList == NULL) {
        return pHandler->EnumPage(pPages);
    }
    for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) {
        CPDF_Dictionary* pKid = pKidList->GetDict(i);
        if (pKid == NULL) {
            continue;
        }
        if (!pKid->KeyExist(FX_BSTRC("Kids"))) {
            if (!pHandler->EnumPage(pKid)) {
                return FALSE;
            }
        } else {
            return _EnumPages(pKid, pHandler);
        }
    }
    return TRUE;
}
void CPDF_Document::EnumPages(IPDF_EnumPageHandler* pHandler)
{
    CPDF_Dictionary* pRoot = GetRoot();
    if (pRoot == NULL) {
        return;
    }
    CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));
    if (pPages == NULL) {
        return;
    }
    _EnumPages(pPages, pHandler);
}
FX_BOOL CPDF_Document::IsContentUsedElsewhere(FX_DWORD objnum, CPDF_Dictionary* pThisPageDict)
{
    for (int i = 0; i < m_PageList.GetSize(); i ++) {
        CPDF_Dictionary* pPageDict = GetPage(i);
        if (pPageDict == pThisPageDict) {
            continue;
        }
        CPDF_Object* pContents = pPageDict ? pPageDict->GetElement(FX_BSTRC("Contents")) : NULL;
        if (pContents == NULL) {
            continue;
        }
        if (pContents->GetDirectType() == PDFOBJ_ARRAY) {
            CPDF_Array* pArray = (CPDF_Array*)pContents->GetDirect();
            for (FX_DWORD j = 0; j < pArray->GetCount(); j ++) {
                CPDF_Reference* pRef = (CPDF_Reference*)pArray->GetElement(j);
                if (pRef->GetRefObjNum() == objnum) {
                    return TRUE;
                }
            }
        } else if (pContents->GetObjNum() == objnum) {
            return TRUE;
        }
    }
    return FALSE;
}
FX_DWORD CPDF_Document::GetUserPermissions(FX_BOOL bCheckRevision) const
{
    if (m_pParser == NULL) {
        return (FX_DWORD) - 1;
    }
    return m_pParser->GetPermissions(bCheckRevision);
}
FX_BOOL CPDF_Document::IsOwner() const
{
    if (m_pParser == NULL) {
        return TRUE;
    }
    return m_pParser->IsOwner();
}
FX_BOOL CPDF_Document::IsFormStream(FX_DWORD objnum, FX_BOOL& bForm) const
{
    {
        CPDF_Object* pObj;
        if (m_IndirectObjs.Lookup((FX_LPVOID)(FX_UINTPTR)objnum, (FX_LPVOID&)pObj)) {
            bForm = pObj->GetType() == PDFOBJ_STREAM &&
                    ((CPDF_Stream*)pObj)->GetDict()->GetString(FX_BSTRC("Subtype")) == FX_BSTRC("Form");
            return TRUE;
        }
    }
    if (m_pParser == NULL) {
        bForm = FALSE;
        return TRUE;
    }
    return m_pParser->IsFormStream(objnum, bForm);
}
void CPDF_Document::ClearPageData()
{
    if (m_pDocPage) {
        CPDF_ModuleMgr::Get()->GetPageModule()->ClearDoc(this);
    }
}
void CPDF_Document::ClearRenderData()
{
    if (m_pDocRender) {
        CPDF_ModuleMgr::Get()->GetRenderModule()->ClearDocData(m_pDocRender);
    }
}
